const mongoose = require("mongoose");



async function createUser(Model, payload) {
  // payload 应该包含必要字段，生产请在外部进行密码哈希
  const doc = await Model.create({
    ...payload,
    createdAt: new Date(),
    updatedAt: new Date(),
  });
  return doc.toObject ? doc.toObject() : doc;
}

async function find(Model, query = {}) {
  /**
        * query.page：前端传来的页码，通常是字符串，例如 "2"
        *parseInt(query.page, 10)：将字符串转换为整数
        *|| 1：如果没有传值或者转换失败，默认页码为 1
        *Math.max(1, ...)：保证页码 至少为 1，避免负数或 0
        效果：page 永远 ≥ 1


        *page： 当前页码
        *limit： 每页显示数量
        *skip： 计算跳过的文档数量，用于分页查询
 */

  const page = Math.max(1, parseInt(query.page, 10) || 1);
  const limit = Math.max(1, parseInt(query.limit, 10) || 10);
  const skip = (page - 1) * limit;

  // 支持按 username 模糊搜索，可扩展 filters
  const filter = {};
  if (query.username) {

    /**
     * 用于模糊查询的正则
     * escapeRegExp 防止用户输入特殊正则字符（如 .*+?^$[]()）   例如用户输入 admin.*，如果不转义，会匹配意外结果
     *  new RegExp (..., "i")  构造不区分大小写的正则表达式
     *  i 表示忽略大小写匹配
     */

    filter.username = new RegExp(escapeRegExp(String(query.username)), "i");
  }

  /**
   * 并行执行两个异步操作：
   * 1. 计算符合过滤条件的文档总数（total）
   * 2. 根据过滤条件查询当前页的数据（data），按创建时间降序排序，跳过前 skip 个文档，限制返回 limit 个文档，并转换为普通对象数组（lean()）
   * 返回一个包含 total、page、limit 和 data 的对象
   * 
   * 
   * Model.countDocuments(filter), 计算符合过滤条件的文档总数
   * Model.find(filter) 根据过滤条件查询文档
   * .sort({ createdAt: -1 }) 按创建时间降序排序
   * .skip(skip) 跳过前 skip 个文档
   * .limit(limit) 限制返回 limit 个文档
   * .lean() 将 Mongoose 文档转换为普通 JavaScript 对象，提升查询性能
   */

  const [total, data] = await Promise.all([
    Model.countDocuments(filter),
    Model.find(filter).sort({ createdAt: -1 }).skip(skip).limit(limit).lean(),
  ]);

  return {
    total,
    page,
    limit,
    data,
  };
}

async function updateById(Model, id, updateObj = {}) {
  if (!id || !mongoose.Types.ObjectId.isValid(id)) {
    throw new Error("Invalid id");
  }
  // 更新时间
  updateObj.updatedAt = new Date();
  const res = await Model.updateOne({ _id: id }, { $set: updateObj });
  return res; // contains matchedCount, modifiedCount (mongoose v6+/v7+)
}

async function deleteById(Model, id) {
  if (!id || !mongoose.Types.ObjectId.isValid(id)) {
    throw new Error("Invalid id");
  }
  const doc = await Model.findByIdAndDelete(id).lean();
  return doc;
}

async function findById(Model, id) {
  if (!id || !mongoose.Types.ObjectId.isValid(id)) {
    throw new Error("Invalid id");
  }
  return Model.findById(id).lean();
}

function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

module.exports = {
  createUser,
  find,
  updateById,
  deleteById,
  findById,
};
